package com.mobilezu.mobile.pushserver.xmpp.session;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmpp.packet.JID;
import org.xmpp.packet.Presence;
import com.mobilezu.mobile.pushserver.service.UserNotFoundException;
import com.mobilezu.mobile.pushserver.xmpp.auth.AuthToken;
import com.mobilezu.mobile.pushserver.xmpp.net.Connection;

public class ClientSession extends Session {

	private static final Log log = LogFactory.getLog(ClientSession.class);
	private static final String ETHERX_NAMESPACE = "http://etherx.jabber.org/streams";
	private AuthToken authToken;
	private boolean initialized;
	private boolean wasAvailable = false;
	private Presence presence = null;

	/**
	 * ������.
	 * 
	 * @param serverName
	 *            the server name
	 * @param connection
	 *            the connection
	 * @param streamID
	 *            the stream ID
	 */
	public ClientSession(String serverName, Connection connection,
			String streamID) {
		super(serverName, connection, streamID);
		presence = new Presence();
		presence.setType(Presence.Type.unavailable);
	}

	/**
	 * ����һ���ͻ��˻Ự.
	 * 
	 * @param serverName
	 *            the server name
	 * @param connection
	 *            the connection
	 * @param xpp
	 *            the XML parser to handle incoming data
	 * @return a newly created session
	 * @throws XmlPullParserException
	 *             if an error occurs while parsing incoming data
	 */
	public static ClientSession createSession(String serverName,
			Connection connection, XmlPullParser xpp)
			throws XmlPullParserException {
		log.debug("createSession()...");

		if (!xpp.getName().equals("stream")) {
			throw new XmlPullParserException("Bad opening tag (not stream)");
		}

		if (!xpp.getNamespace(xpp.getPrefix()).equals(ETHERX_NAMESPACE)) {
			throw new XmlPullParserException("Stream not in correct namespace");
		}

		String language = "en";
		for (int i = 0; i < xpp.getAttributeCount(); i++) {
			if ("lang".equals(xpp.getAttributeName(i))) {
				language = xpp.getAttributeValue(i);
			}
		}

		// Store language and version information
		connection.setLanaguage(language);
		connection.setXMPPVersion(MAJOR_VERSION, MINOR_VERSION);

		// Create a ClientSession
		ClientSession session = SessionManager.getInstance()
				.createClientSession(connection);

		// Build the start packet response
		StringBuilder sb = new StringBuilder(200);
		sb.append("<?xml version='1.0' encoding='UTF-8'?>");
		sb.append("<stream:stream ");
		sb.append("xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\" from=\"");
		sb.append(serverName);
		sb.append("\" id=\"");
		sb.append(session.getStreamID().toString());
		sb.append("\" xml:lang=\"");
		sb.append(language);
		sb.append("\" version=\"");
		sb.append(MAJOR_VERSION).append(".").append(MINOR_VERSION);
		sb.append("\">");
		connection.deliverRawText(sb.toString());

		// XMPP 1.0 needs stream features
		sb = new StringBuilder();
		sb.append("<stream:features>");
		if (connection.getTlsPolicy() != Connection.TLSPolicy.disabled) {
			sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
			if (connection.getTlsPolicy() == Connection.TLSPolicy.required) {
				sb.append("<required/>");
			}
			sb.append("</starttls>");
		}

		String specificFeatures = session.getAvailableStreamFeatures();
		if (specificFeatures != null) {
			sb.append(specificFeatures);
		}
		sb.append("</stream:features>");

		connection.deliverRawText(sb.toString());
		return session;
	}

	/**
	 * ��ȡ��username�ĻỰ.
	 * 
	 * @return the username
	 * @throws UserNotFoundException
	 *             if a user has not authenticated yet
	 */
	public String getUsername() throws UserNotFoundException {
		if (authToken == null) {
			throw new UserNotFoundException();
		}
		return getAddress().getNode();
	}

	/**
	 * ��ȡ�Ự��ص������֤����
	 * 
	 * @return the authentication token
	 */
	public AuthToken getAuthToken() {
		return authToken;
	}

	/**
	 * ���ûỰ��ص������֤����
	 * 
	 * @param authToken
	 *            the authentication token
	 */
	public void setAuthToken(AuthToken authToken) {
		this.authToken = authToken;
	}

	/**
	 * ���ûỰ��ص������֤����.
	 * 
	 * @param authToken
	 *            the authentication token
	 * @param resource
	 *            the resource
	 */
	public void setAuthToken(AuthToken authToken, String resource) {
		setAddress(new JID(authToken.getUsername(), getServerName(), resource));
		this.authToken = authToken;
		setStatus(Session.STATUS_AUTHENTICATED);
		// Add session to the session manager
		sessionManager.addSession(this);
	}

	/**
	 * �жϻỰ�Ƿ񱻳�ʼ��
	 * 
	 * @return true if the session has been initialized, false otherwise.
	 */
	public boolean isInitialized() {
		return initialized;
	}

	/**
	 * ���ûỰ��ʼ��״̬.
	 * 
	 * @param initialized
	 *            true if the session has been initialized
	 */
	public void setInitialized(boolean initialized) {
		this.initialized = initialized;
	}

	/**
	 * �жϻỰ�Ƿ����.
	 * 
	 * @return true if the session was available ever, false otherwise.
	 */
	public boolean wasAvailable() {
		return wasAvailable;
	}

	/**
	 * ȡ�ô��ڵĻỰ
	 * 
	 * @return the presence
	 */
	public Presence getPresence() {
		return presence;
	}

	/**
	 * ���ûỰ�Ĵ���
	 * 
	 * @param presence
	 *            the presence
	 */
	public void setPresence(Presence presence) {
		Presence oldPresence = this.presence;
		this.presence = presence;
		if (oldPresence.isAvailable() && !this.presence.isAvailable()) {
			setInitialized(false);
		} else if (!oldPresence.isAvailable() && this.presence.isAvailable()) {
			wasAvailable = true;
		}
	}

	/**
	 * ��ȡ���������ܵ��ı�
	 */
	public String getAvailableStreamFeatures() {
		StringBuilder sb = new StringBuilder();
		if (getAuthToken() == null) {
			// Supports Non-SASL Authentication
			sb.append("<auth xmlns=\"http://jabber.org/features/iq-auth\"/>");
			// Supports In-Band Registration
			sb.append("<register xmlns=\"http://jabber.org/features/iq-register\"/>");
		} else {
			// If the session has been authenticated
			sb.append("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>");
			sb.append("<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>");
		}
		return sb.toString();
	}

	@Override
	public String toString() {
		return super.toString() + " presence: " + presence;
	}

}
